Skip to content

feat(sdks): close Node and Java per-task policy and batching parity#436

Merged
pratyush618 merged 13 commits into
masterfrom
fix/node-in-flight-bound
Jul 16, 2026
Merged

feat(sdks): close Node and Java per-task policy and batching parity#436
pratyush618 merged 13 commits into
masterfrom
fix/node-in-flight-bound

Conversation

@pratyush618

@pratyush618 pratyush618 commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Closes the Node/Java half of the parity debt each scaling tier left behind. One live bug, the rest missing surface.

The bug

NodeDispatcher::run spawned every job it was handed and immediately took the next, and this SDK never set max_in_flight — Python and Java both do. A channel that never fills applies no backpressure, so nothing capped in-flight work: the worker claimed jobs it could not run, stranding them Running while peers sharing the database skipped them. Same over-claim class as S01. types.ts already documented a bound that did not exist.

Fixed with a concurrency option, separate from channelCapacity and mirroring Java. It bounds what the scheduler claims and what the dispatcher runs — the dispatcher permit matters on its own because it is the only bound on the mesh path, where stolen jobs never reach this scheduler's in-flight accounting. Opt-in: unset stays unbounded, so no existing worker silently acquires a cap. channelCapacity's doc now says what it does.

Per-task policy — all four knobs, all three SDKs

Java surfaced none of the four. The hardcoded Nones were not a lazy binding: the wire struct had no field, so nothing upstream could send a value.

knob Python Node Java
rateLimit had it had it new
maxConcurrent had it had it new
retryBudget #435 new new
maxInFlightPerTask #433 new new

No binding hardcodes a None the caller cannot reach any more. On Java these live on Task and @TaskHandler, beside the retry curve and circuit breaker that already reach the worker that way. A malformed rate spec now fails the worker's start, naming the task and the option, rather than running it unthrottled — registration gained an error path for that.

Two "hand-list every field" traps removed, not extended

  • Node: the config collector gated on five named options, so a task setting only a new option was dropped before it reached the scheduler — silently, and invisibly to type-checking. It now derives from the built config.
  • Java: the encoder merged two maps by name, so each knob needed its own map, its own capture, and its own arm of a name union. Worker now holds the tasks that carry policy, so the encoder reads one source.

Both paid off inside this PR: when retryBudget unblocked, Node needed no gate edit and Java needed one if in one encoder.

Result batching

Both SDKs handled one result per wake, so every job cost its own transaction. They now finalize everything already queued in one, matching Python.

Java caps the batch explicitly. Its result channel is deliberately unbounded so the dispatcher never blocks a runtime worker, which means Python's "the channel is bounded, so the drain caps itself" reasoning does not transfer — inheriting it uncritically was the trap. Node's channel is bounded, so it needs no cap. Java's JNI local frame stays per outcome; sized for one callback, it would overflow around a batch.

Verification

Every fix has a test watched failing with the fix removed:

test without the fix
Node in-flight bound expected 12 to be less than or equal to 3
Node config gate expected 4 to be 1
Java policy (×4) all fail without the wiring
Node/Java retryBudget budget kill never reaches the DLQ
mixed-batch outcomes — asserts one outcome per job, none lost or doubled

The Node backpressure test sets batchSize to the whole backlog on purpose: several bounds overlap (poll cadence, task duration, channel capacity, in-flight cap) and the test proves nothing unless the one under test is the tightest. An earlier version passed without the fix because the ~50ms poll cadence was the real limiter.

Node 404 tests, typecheck + biome clean. Java ./gradlew build (tests + spotless + checkstyle) plus plainJavadocJar, which gates publishing rather than build. Rust 190 tests across default/postgres/redis/native-async, clippy + fmt clean.

Follow-ups, deliberately not here

  • Task's constructor went 7 → 11 positional args following the existing wither idiom. The next knob makes it 12; it wants grouping into a TaskPolicy first. Separate concern from surfacing options.
  • Cursor pagination (S12) and Java middleware toggles are the parity debt still open.

Summary by CodeRabbit

  • New Features
    • Added per-task scheduling controls: rateLimit, retryBudget, maxConcurrent, and maxInFlightPerTask (Java SDK, Node worker, and native task policy).
    • Added worker-level concurrency limiting to cap simultaneous job execution.
    • Improved outcome processing by finalizing scheduler results in batches.
  • Bug Fixes
    • Malformed rateLimit/policy specs now fail worker startup early with scope-aware validation errors.
    • Enforced retry budgets with dead-letter outcomes tagged for exhausted budgets.
  • Tests
    • Added/expanded Java and Node tests covering batching correctness, backpressure/concurrency limits, and policy/config validation.

The dispatcher spawned every job it was handed and immediately took the next,
so the job channel drained as fast as the scheduler filled it. A channel that
never fills applies no backpressure, and this SDK never set max_in_flight —
which Python and Java both do — so nothing capped in-flight work. The worker
claimed jobs it could not run, stranding them Running while peers sharing the
database skipped them.

Add a `concurrency` option, separate from `channelCapacity`, mirroring Java:
it caps what the scheduler claims and what the dispatcher runs. The dispatcher
permit is the only bound on the mesh path, where stolen jobs never reach this
scheduler's in-flight accounting.

Opt-in — unset stays unbounded, so no existing worker silently acquires a cap.
channelCapacity's doc claimed to be the in-flight bound and never was; it now
says what it does.
Python has had this since the per-task bulkhead landed; this SDK hardcoded
None, so one slow task could occupy a worker's whole pool.

The config collector gated on a hand-listed set of option names, which drops
any option missing from the list — silently, and invisibly to type-checking.
Derive the gate from the built config instead, so a new option cannot be lost
the next time. Name the field in the rate-spec error too, now that more than
one option shares that grammar.
Python and Node have had both for a while; this SDK carried neither. The
hardcoded Nones were not a lazy binding — the wire struct had no field for
them, so nothing upstream could send one.

Available on Task and on @TaskHandler, matching how the retry curve and
circuit breaker already reach the worker. A malformed rate spec now fails the
worker's start, naming the task and option, rather than running the task
unthrottled; registration gained an error path to allow that.

Worker holds the tasks that carry policy rather than a map per knob, so the
encoder reads one source: the next knob is encoded in one place instead of
needing its own map, its own capture, and its own arm of a name union.
The drain loop handled one result per wake, so each job cost its own
transaction. Take everything already queued and finalize it in one, matching
Python. The channel is bounded, so the drain caps itself.

Successes batch into a single write while failures stay per-result, and one
outcome still comes back per job in order, so events and middleware fire
exactly once each. The catch_unwind now guards a batch, widening a panic from
one dropped outcome to N — acceptable because the realistic failure is a batch
write error, which already falls back to finalizing job by job.
The drain loop handled one result per wake, so each job cost its own
transaction. Take everything already queued and finalize it in one, matching
Python and Node.

The batch is capped explicitly rather than drained until empty: unlike the
other shells, this result channel is unbounded so the dispatcher never blocks
a runtime worker, which means nothing else would bound one drain. The JNI
local frame stays per outcome — sized for a single callback, it would overflow
around a batch.
Caps how fast a task may retry across all of its jobs; once spent, failures
dead-letter rather than retrying. Landed for Python in #435.

Nothing was added to the config gate for it — the gate derives from the built
config, so a new option reaches the scheduler on its own. The rate parser now
names the field, since two options share its "100/m" grammar.
Caps how fast a task may retry across all of its jobs; once spent, failures
dead-letter rather than retrying. Landed for Python in #435.

On Task and @TaskHandler beside the rate limit, sharing its parser and its
fail-fast on a malformed spec. Completes this SDK's per-task policy surface.
The last of the four per-task knobs this SDK carried no wire field for. All
three SDKs now surface every one, so no binding hardcodes a None the caller
cannot reach.
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5747702f-251e-4ae3-9ac3-cc14360f4d26

📥 Commits

Reviewing files that changed from the base of the PR and between 53ae43f and 355fab7.

📒 Files selected for processing (1)
  • sdks/node/test/worker/rateScope.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdks/node/test/worker/rateScope.test.ts

📝 Walkthrough

Walkthrough

Adds end-to-end task policy configuration for rate limits, retry budgets, concurrency caps, and per-task in-flight limits across Java and Node APIs. Worker dispatch is concurrency-bounded, results are processed in batches, and validation covers enforcement and startup errors.

Changes

Task policy runtime

Layer / File(s) Summary
Policy contracts and task state
crates/taskito-java/src/convert.rs, crates/taskito-node/src/config.rs, sdks/java/..., sdks/node/src/types.ts
Adds policy fields to Java and Node configuration surfaces, including Java task propagation and accessors.
Policy encoding and configuration conversion
sdks/java/..., crates/taskito-node/src/convert/..., sdks/node/src/worker.ts
Encodes task policies and parses rate specifications, retry budgets, and in-flight limits into native configuration.
Concurrency, registration, and result batching
crates/taskito-java/src/worker.rs, crates/taskito-node/src/{dispatcher,worker}.rs
Validates and registers Java policies, bounds Node execution, and processes outcomes in bounded batches.
Policy and batching validation
sdks/java/src/test/..., sdks/node/test/worker/*
Tests concurrency, throttling, retry budgets, malformed specifications, scoped errors, and exact-once outcomes.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Worker
  participant Dispatcher
  participant Scheduler
  participant Callback
  Client->>Worker: configure task policies and concurrency
  Worker->>Dispatcher: start bounded execution
  Dispatcher->>Scheduler: submit completed result batch
  Scheduler->>Worker: return ordered outcomes
  Worker->>Callback: report each outcome
Loading

Possibly related PRs

  • ByteVeda/taskito#311: Extends the Java task policy configuration and worker registration introduced there.
  • ByteVeda/taskito#430: Provides scheduler result-batching APIs used by the worker drain paths.
  • ByteVeda/taskito#433: Implements scheduler enforcement for the per-task in-flight limit wired here.

Suggested labels: scheduler

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.47% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: Node and Java parity for per-task policies and batching.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/node-in-flight-bound

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (3)
sdks/java/src/test/java/org/byteveda/taskito/core/TaskPolicyConfigTest.java (1)

23-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise maxConcurrent across two workers.

This single-worker test also passes if the policy is mistakenly implemented as worker-local maxInFlightPerTask. Start two workers against the same database and assert their shared peak remains one to validate the cluster-wide contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/java/src/test/java/org/byteveda/taskito/core/TaskPolicyConfigTest.java`
around lines 23 - 58, Extend maxConcurrentSerializesOneTask to start two workers
against the same database and enqueue enough solo jobs to exercise both workers,
while retaining the running/peak tracking across both handlers. Await completion
for every job, then assert the shared peak remains one, ensuring maxConcurrent
is enforced cluster-wide rather than per worker.
sdks/node/test/worker/backpressure.test.ts (1)

29-64: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Test the dispatcher limit independently from scheduler claim bounding.

This test still passes without the semaphore because SchedulerConfig.max_in_flight is also set to concurrency. Add a direct dispatcher test or a mesh-enabled case where stolen jobs bypass this scheduler’s accounting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/node/test/worker/backpressure.test.ts` around lines 29 - 64, Update the
concurrency test around queue.runWorker so it validates the dispatcher’s own
in-flight limit independently of SchedulerConfig.max_in_flight. Add a direct
dispatcher-focused setup or enable a mesh/stolen-job path that bypasses
scheduler accounting, then retain assertions that peak running jobs stays at or
below concurrency and exceeds one.
sdks/java/src/main/java/org/byteveda/taskito/task/Task.java (1)

52-258: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Repeated 11-arg constructor call across every wither method.

Every copy-site (withOptions, retryPolicy, codecs, idempotent, circuitBreaker, rateLimit, maxConcurrent, retryBudget, maxInFlightPerTask, plus both of(...) factories) hand-duplicates the same 11-argument new Task<>(...) call. All sites are correctly wired today, but this is exactly the failure mode the PR fixes on the Node side (buildTaskConfigs/setsSomething deriving from a single built config so a new option can't be silently dropped) — here, adding the next policy field means touching 11 call sites by hand, and a missed one fails silently (wrong field slot, no compile error since all fields share compatible types in places).

Consider a private "copy" helper (e.g. a mutable holder or an internal builder) that every fluent method delegates to, so a new field is added in one place.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/java/src/main/java/org/byteveda/taskito/task/Task.java` around lines 52
- 258, Centralize Task copy construction to eliminate the repeated 11-argument
new Task calls across the of factories and fluent methods including withOptions,
retryPolicy, codecs, idempotent, circuitBreaker, rateLimit, maxConcurrent,
retryBudget, and maxInFlightPerTask. Add a private copy/helper or internal
builder that preserves all existing fields and lets each method override only
its targeted value, then delegate every factory and wither through it so future
fields are wired in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/taskito-java/src/worker.rs`:
- Line 109: Move the fallible register_task_policies call before
register_live_worker and register_subscriptions in the worker initialization
flow, so malformed rate specifications are rejected before persistent worker
state or subscriptions are created. Preserve the existing policy registration
behavior and successful initialization order after validation.

In `@crates/taskito-node/src/convert/task_config.rs`:
- Around line 18-25: Update parse_rate_spec and its callers to accept the
configured task name from input.name, then include that task name alongside the
option field in malformed-policy errors. Apply the same task-aware error context
to the related parsing path noted in the comment, while preserving the existing
validation and fail-fast behavior.
- Line 48: Update the max_in_flight_per_task conversion to map 0 to None for
uncapped behavior, preserve positive values as usize, and reject negative values
rather than clamping them to 1. Change only the conversion logic for
max_in_flight_per_task.

In `@crates/taskito-node/src/worker.rs`:
- Around line 201-208: Limit the result collection in the worker loop around
result_rx.recv() and result_rx.try_recv() to at most the configured channel
capacity. Stop draining once the batch reaches that capacity, while preserving
the existing batched finalization behavior for smaller batches.

In `@sdks/java/src/main/java/org/byteveda/taskito/task/Task.java`:
- Around line 202-215: The maxConcurrent method must reject zero as a
concurrency cap while preserving null as the uncapped value. Update
Task.maxConcurrent to normalize a zero input to null before constructing the new
Task, leaving positive limits unchanged.

In `@sdks/node/test/worker/resultBatching.test.ts`:
- Around line 61-64: Extend the result-batching assertions around completed and
dead job IDs to verify the two collections are disjoint, matching the Java
test’s overlap check. Preserve the existing length and per-collection uniqueness
assertions, and add an assertion that no ID appears in both completed and dead.
- Around line 52-57: Update the test setup around the enqueue loop and
queue.runWorker call so every asynchronous queue.enqueue operation is awaited
before the worker starts. Collect or await both enqueues for each iteration,
then invoke runWorker only after the complete burst is queued, preserving the
existing concurrency and batchSize options.

---

Nitpick comments:
In `@sdks/java/src/main/java/org/byteveda/taskito/task/Task.java`:
- Around line 52-258: Centralize Task copy construction to eliminate the
repeated 11-argument new Task calls across the of factories and fluent methods
including withOptions, retryPolicy, codecs, idempotent, circuitBreaker,
rateLimit, maxConcurrent, retryBudget, and maxInFlightPerTask. Add a private
copy/helper or internal builder that preserves all existing fields and lets each
method override only its targeted value, then delegate every factory and wither
through it so future fields are wired in one place.

In `@sdks/java/src/test/java/org/byteveda/taskito/core/TaskPolicyConfigTest.java`:
- Around line 23-58: Extend maxConcurrentSerializesOneTask to start two workers
against the same database and enqueue enough solo jobs to exercise both workers,
while retaining the running/peak tracking across both handlers. Await completion
for every job, then assert the shared peak remains one, ensuring maxConcurrent
is enforced cluster-wide rather than per worker.

In `@sdks/node/test/worker/backpressure.test.ts`:
- Around line 29-64: Update the concurrency test around queue.runWorker so it
validates the dispatcher’s own in-flight limit independently of
SchedulerConfig.max_in_flight. Add a direct dispatcher-focused setup or enable a
mesh/stolen-job path that bypasses scheduler accounting, then retain assertions
that peak running jobs stays at or below concurrency and exceeds one.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5b8c29d6-c1dd-42aa-83f3-41437e1b6cc9

📥 Commits

Reviewing files that changed from the base of the PR and between 2a02224 and 630caab.

📒 Files selected for processing (17)
  • crates/taskito-java/src/convert.rs
  • crates/taskito-java/src/worker.rs
  • crates/taskito-node/src/config.rs
  • crates/taskito-node/src/convert/task_config.rs
  • crates/taskito-node/src/dispatcher.rs
  • crates/taskito-node/src/worker.rs
  • sdks/java/processor/src/main/java/org/byteveda/taskito/processor/TaskHandlerProcessor.java
  • sdks/java/src/main/java/org/byteveda/taskito/annotation/TaskHandler.java
  • sdks/java/src/main/java/org/byteveda/taskito/task/Task.java
  • sdks/java/src/main/java/org/byteveda/taskito/worker/Worker.java
  • sdks/java/src/test/java/org/byteveda/taskito/core/ResultBatchingTest.java
  • sdks/java/src/test/java/org/byteveda/taskito/core/TaskPolicyConfigTest.java
  • sdks/node/src/types.ts
  • sdks/node/src/worker.ts
  • sdks/node/test/worker/backpressure.test.ts
  • sdks/node/test/worker/resultBatching.test.ts
  • sdks/node/test/worker/taskConfig.test.ts

Comment thread crates/taskito-java/src/worker.rs Outdated
Comment thread crates/taskito-node/src/convert/task_config.rs Outdated
Comment thread crates/taskito-node/src/convert/task_config.rs
Comment thread crates/taskito-node/src/worker.rs
Comment thread sdks/java/src/main/java/org/byteveda/taskito/task/Task.java
Comment thread sdks/node/test/worker/resultBatching.test.ts
Comment thread sdks/node/test/worker/resultBatching.test.ts
A malformed rate spec was rejected after the worker row and its subscriptions
were already written, and the start then returned without a handle — so the
lifecycle loop that removes them never ran and both rows leaked.

Build the policies first: parsing is pure, so a rejected config now leaves
nothing behind.
A bounded channel caps how many results sit in it at once, not how many one
drain takes: senders refill slots while try_recv runs, so the loop could
swallow a whole backlog into one Vec and stall finalization behind it. Cap it
like the Java drain does.
A malformed spec on Node named the option but not the task, so a start-up
failure could not say which config was wrong. Java already named both.

Task.maxConcurrent(0)/maxInFlightPerTask(0) stored the zero verbatim, and the
scheduler reads a zero cap as "never dispatch". Zero is the annotation's unset
sentinel, so the fluent setters now read it the same way.

Also awaits the enqueues in the batching test — they could still be pending
when the worker started — and asserts the terminal ids are disjoint, which
per-list uniqueness would miss.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/taskito-node/src/convert/task_config.rs`:
- Around line 19-31: Update parse_rate_spec to accept a scope/resource label
alongside field and name, and use it in the invalid_arg message instead of
hardcoding “task”; update both task and queue call sites, including the usage
around line 94, to pass the appropriate “task” or “queue” scope so malformed
queue limits identify the queue correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 94e63b61-a58c-4d72-aa10-7b751341520f

📥 Commits

Reviewing files that changed from the base of the PR and between 630caab and 7457e9e.

📒 Files selected for processing (5)
  • crates/taskito-java/src/worker.rs
  • crates/taskito-node/src/convert/task_config.rs
  • crates/taskito-node/src/worker.rs
  • sdks/java/src/main/java/org/byteveda/taskito/task/Task.java
  • sdks/node/test/worker/resultBatching.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • sdks/node/test/worker/resultBatching.test.ts
  • crates/taskito-node/src/worker.rs
  • sdks/java/src/main/java/org/byteveda/taskito/task/Task.java
  • crates/taskito-java/src/worker.rs

Comment thread crates/taskito-node/src/convert/task_config.rs Outdated
Queues configure a rate limit through the same parser, so wording the error
"on task" reported a malformed queue limit as a task — a regression from
adding the name in the first place. Pass the kind alongside the name.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sdks/node/test/worker/rateScope.test.ts`:
- Line 14: Strengthen the assertions around queue.runWorker in the rate-scope
tests so they verify the complete error scope, not merely a substring. Capture
the thrown error message once for the task-scoped and queue-scoped cases, then
assert the expected task or queue scope is present and the opposite scope is
absent, preserving the distinction required by the contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eab5c3a7-0163-4115-ae68-269c959d15d0

📥 Commits

Reviewing files that changed from the base of the PR and between 7457e9e and 53ae43f.

📒 Files selected for processing (2)
  • crates/taskito-node/src/convert/task_config.rs
  • sdks/node/test/worker/rateScope.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/taskito-node/src/convert/task_config.rs

Comment thread sdks/node/test/worker/rateScope.test.ts Outdated
toThrow(string) only checks containment, so a message naming both scopes would
have satisfied it — and naming exactly one is the contract under test. Compare
the whole message.
@pratyush618
pratyush618 merged commit a112890 into master Jul 16, 2026
36 checks passed
@pratyush618
pratyush618 deleted the fix/node-in-flight-bound branch July 16, 2026 12:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant